#pragma once
#include <string>
#if !defined(__GNUC__)
#include <random>
#else
#include <limits>
#include <stdexcept>
#endif
#include <vector>
#include <algorithm>
#include <cmath>

using std::string;
using std::vector;

//fwd decl
//class string;

namespace EQMCHelpers
{
}

class MathClass
{
public:
    static double Abs(const double l) { return std::abs(l); }
    static double Sqrt(const double l) { return std::sqrt(l); }
    static double Log(const double l) { return std::log(l); }
    static double Max(const double l, const double r) { return l < r ? r : l; }
    static double Min(const double l, const double r) { return l < r ? l : r; }
};

static MathClass Math;

std::string to_string(double d);

//namespace EQMCSharpPayoffs
//{

class EQMCBase
{
public:
    typedef std::invalid_argument Exception;

    enum EventType { SCH1, SCH2 };

    virtual void initData() = 0;
    virtual double calcCorePayoffBi(const int trial, bool isPastPayoff) const = 0;
    virtual double run() const = 0;

    void init(int trials_, int unds_, int t_);

    EQMCBase() : path(NULL) {}
    ~EQMCBase() { delete path; }

    int getInt(const string& name, int def) const;
    double getDouble(const string& name, double def) const;
    string getString(const string& name, const string& def) const;
    double yearFraction(int t1, int t2, const string& dayCountBasis) const;
    inline bool isEvent(const int t, EventType type) const
    {
        if (type == SCH1)
            return t == 51;
        else if (type == SCH2)
        {
            switch (t)
            {
            case 1193:
            case 1213:
            case 1233:
            case 1248:
            case 1266:
            case 1286:
            case 1307:
            case 1323:
            case 1344:
            case 1365:
            case 1385:
            case 1406:
            case 1424:
                return true;
            }
        }
        return false;
    }

    int nObs;
    int nUnds;
    int nSch2s;
    int trials;
    double weight(const int und) const { return 0.2; }
    double strikeFactor;

    //mutable int trial;
    double* path;

    //double NS(const int trial, const int und, const int t) const { return path[trial, und, t]; }
    double NS(const int trial, const int und, const int t) const { return path[trial*(nObs + 1)*nUnds + und*(nObs + 1) + t]; }
    double& setNS(const int trial, const int und, const int t) { return path[trial*(nObs + 1)*nUnds + und*(nObs + 1) + t]; }

    enum DerType { PUT, CALL, FWD };
    //DerType PUT{ get{ return DerType.PUT; } }
    //DerType CALL{ get{ return DerType.CALL; } }
    //DerType FWD{ get{ return DerType.FWD; } }
    DerType derivativeType;

};
//}

//namespace userPayoff
//{
//	class userFunctions 
//	{
//	public:
//		void initData();
//		void calcCorePayoffBi(const int trial, bool isPastPayoff);
//	};
//}
